home *** CD-ROM | disk | FTP | other *** search
Text File | 1998-04-30 | 21.5 KB | 922 lines | [TEXT/MPS ] |
- /*
- File: ATPSample.cp
-
- Contains: Sample program for the ATP endpoint
-
- Copyright: © 1993-1995 by Apple Computer, Inc., all rights reserved.
-
- */
-
- #ifndef __OPENTPTGLOBALNEW__
- #include <OpenTptGlobalNew.h>
- #endif
- #ifndef __OPENTPTAPPLETALK__
- #include <OpenTptAppleTalk.h>
- #endif
- #ifndef __ATALKSAMPLEUTILS__
- #include "ATalkSampleUtils.h"
- #endif
- #ifndef __STDIO__
- #include <stdio.h>
- #endif
- #ifndef __EVENTS__
- #include <Events.h>
- #endif
-
- /*******************************************************************************
- ** Structures
- ********************************************************************************/
-
- typedef struct TOutgoingRequest TOutgoingRequest;
-
- struct TOutgoingRequest
- {
- TOutgoingRequest* fLink; // Link for list
- TUnitRequest fRequest; // The actual Request;
- Boolean fHasReply; // Flag for when reply comes in
- };
-
- typedef struct TIncomingRequest TIncomingRequest;
-
- struct TIncomingRequest
- {
- TIncomingRequest* fLink; // Link for list
- TUnitRequest fRequest;
- TUnitReply fReply;
- DDPAddress fAddr; // Buffer for the address
- Boolean fNeedsReply;
- Boolean fIsXO;
- Boolean fNeedsMore;
- };
-
- /*******************************************************************************
- ** GlobalVariables
- ********************************************************************************/
-
- const char* kRequestorName = "ATPRequestor:WorkStation";
- const char* kResponderName = "ATPResponder:WorkStation";
- const size_t kRequestSize = 578 + 4;
- const size_t kMaxRequests = 4;
- const size_t kMaxReplyBufLen = (578 * 8) + 4;
- const size_t kTestReplySize = (578 * 8) + 4;
-
- static char gRequestData[] =
- " This is a test of the Emergency Broadcast System. \
- This is only a test. If this had been an actual emergency, you would have been \
- instructed where to tune in your area for more information.";
-
- static UInt8 gReplyBuffer[kMaxReplyBufLen];
- static UInt8 gRequestBuffer[kRequestSize];
- //
- // Used for Bind, Resolve, and unbind
- //
- OTEventCode gFunctionEvent;
- Boolean gFunctionComplete;
- OSStatus gFunctionErr;
- void* gFunctionCookie;
- int gTestMode;
-
- TOutgoingRequest* gOutgoingRequestList;
- TIncomingRequest* gIncomingRequestList;
- size_t gOutgoingRequests;
- size_t gIncomingRequests;
-
-
- long gReqSequence=0;
-
- OTTimeStamp gStartStamp;
- SInt32 gStartTicks;
- size_t gTotalBytes;
- size_t gDotCount = 0;
- size_t gLineCount = 0;
- DDPAddress gPeerAddress;
-
- Boolean gFlowControlled;
- Boolean gIncomingRequestAvailable = false;
-
- /*******************************************************************************
- ** DoStaticBind w/ Name Registration
- ** Static bind is where we let the endpoint choose our network
- ** address.
- **
- ** NOTE: no relationship between Static bind and async!
- ********************************************************************************/
-
- OSStatus DoStaticBind(EndpointRef atpEp, const char* name)
- {
- OSStatus err = kOTNoError;
- DDPNBPAddress myAddress; // To set up my address for the bind
- DDPAddress addr; // To hold my address when we're done
- //
- // Initialize my address
- //
- myAddress.Init(0, 0, 0); // Source address & type
- //
- // Create the TBind for the request, holding my address
- //
- TBind req;
- //
- // Set my name into the NBP address, and init the TBind with
- // the address (SetNBPEntity returns the size of the address).
- //
- req.addr.buf = (UInt8*)&myAddress;
- req.addr.len = myAddress.SetNBPEntity(name);
- req.qlen = 0;
- //
- // Create the TBind for the return information
- //
- TBind ret;
-
- ret.addr.buf = (UInt8*)&addr;
- ret.addr.maxlen = sizeof(addr);
-
- fprintf(stderr, "Doing Bind\n");
- //
- // Try to bind
- //
- gFunctionComplete = false;
-
- err = OTBind(atpEp, &req, &ret);
- if ( err != kOTNoError )
- {
- fprintf(stderr, "OTBind: returned error %d\n", err);
- return err;
- }
- //
- // Wait for the bind to be complete
- //
- while ( !gFunctionComplete )
- OTIdle();
-
- if ( gFunctionEvent != T_BINDCOMPLETE )
- {
- fprintf(stderr, "OTBind: completed with event %08lX instead of %08lX\n",
- gFunctionEvent, T_BINDCOMPLETE);
- return kOTProtocolErr;
- }
-
- if ( gFunctionErr != kOTNoError )
- {
- fprintf(stderr, "OTBind: completed with error %d\n", gFunctionErr);
- return gFunctionErr;
- }
- fprintf(stderr, "Bound address = ");
- ShowDDPAddress(&addr);
- fprintf(stderr, "\n");
- fprintf(stderr, "Bound queue len is %d\n", ret.qlen);
-
- fprintf(stderr, "After Bind, ");
- ShowEndpointState(atpEp, "");
-
- return kOTNoError;
- }
-
- /*******************************************************************************
- ** DoResolveAddr
- ** Find someone to talk to
- ********************************************************************************/
-
- OSStatus DoResolveAddr(EndpointRef atpEp, const char* name, DDPAddress* peer)
- {
- OSStatus err = kOTNoError;
- NBPAddress peerAddress;
- //
- // Create the TBinds for the request and return information
- // (Init sets the address and returns the size)
- //
- TBind req;
-
- req.addr.buf = (UInt8*)&peerAddress;
- req.addr.len = peerAddress.Init(name);
- req.qlen = 0;
-
- TBind ret;
-
- ret.addr.buf = (UInt8*)peer;
- ret.addr.maxlen = sizeof(DDPAddress);
- //
- // Try to resolve the address - wait 2 seconds
- //
- gFunctionComplete = false;
- err = OTResolveAddress(atpEp, &req, &ret, 2000);
- if ( err != kOTNoError )
- {
- fprintf(stderr, "OTResolveAddress: returned error %d\n", err);
- return err;
- }
- //
- // Wait for something to happen
- //
- while ( !gFunctionComplete )
- ;
-
- if ( gFunctionEvent != T_RESOLVEADDRCOMPLETE )
- {
- fprintf(stderr, "OTResolveAddress: completed with event %08lX instead of %08lX\n",
- gFunctionEvent, T_RESOLVEADDRCOMPLETE);
- return kOTProtocolErr;
- }
-
- if ( gFunctionErr != kOTNoError )
- {
- return gFunctionErr;
- }
- //
- // Copy my peer's address into the global
- //
- fprintf(stderr, "Partner's address = ");
- ShowDDPAddress(peer);
- fprintf(stderr, "\n");
- fflush(stderr);
- return kOTNoError;
- }
-
- /*******************************************************************************
- ** DoSendUReplies
- ********************************************************************************/
-
- void DoSendUReplies(EndpointRef atpEp)
- {
- OSStatus err;
-
- if ( gFlowControlled )
- return;
-
- /* -------------------------------------------------------------------------
- Now, let's send replies to any new incoming requests
- ------------------------------------------------------------------------- */
-
- TIncomingRequest* next = gIncomingRequestList;
- TIncomingRequest* prev = NULL;
-
- while ( next != NULL )
- {
- if ( next->fNeedsReply )
- {
- TUnitReply* reply = &next->fReply;
- reply->opt.len = 0;
- reply->udata.len = sizeof(gReplyBuffer);
- reply->udata.buf = gReplyBuffer;
- reply->sequence = next->fRequest.sequence;
- OTMemset(reply->udata.buf, (int)next->fRequest.sequence, sizeof(gReplyBuffer));
-
- gFlowControlled = true;
-
- err = OTSndUReply(atpEp, reply, (OTFlags)0);
-
- if ( err != kOTFlowErr )
- gFlowControlled = false;
- else
- return;
-
- if ( err == kOTNoError )
- {
- gTotalBytes += sizeof(gReplyBuffer);
- next->fNeedsReply = false;
- gDotCount += 1;
- prev = next;
- next = next->fLink;
- }
- else
- {
- fprintf(stderr, "OTSndUReply: returned error %d\n", err);
- //
- // If we got a memory-type error - we can recover.
- // Otherwise, we don't know what's going on, and
- // we'll just blow away the incoming request and
- // never answer it.
- //
- if ( err != kENOMEMErr && err != kENOSRErr )
- {
- if ( prev == NULL )
- {
- gIncomingRequestList = next->fLink;
- }
- else
- {
- prev->fLink = next->fLink;
- }
- gIncomingRequests -= 1;
- delete next;
- }
- }
- }
- else
- {
- prev = next;
- next = next->fLink;
- }
- }
- }
-
- /*******************************************************************************
- ** DoReadURequests
- ********************************************************************************/
-
- void DoReadURequests(EndpointRef atpEp)
- {
- OTFlags flags;
-
- while ( true )
- {
- TIncomingRequest* req = new TIncomingRequest;
-
- if ( req == NULL )
- {
- fprintf(stderr, "DoReadURequest: can't allocate incoming request structure\n");
- //
- // Since we didn't read to a kOTNoDataErr, set the flag back to indicate that
- // we still have requests to read
- //
- gIncomingRequestAvailable = true;
- return;
- }
-
- req->fNeedsReply = false;
-
- req->fRequest.addr.buf = (UInt8*)&req->fAddr;
- req->fRequest.addr.maxlen = sizeof(req->fAddr);
- req->fRequest.opt.maxlen = 0;
- req->fRequest.udata.buf = gRequestBuffer;
- req->fRequest.udata.maxlen = sizeof(gRequestBuffer);
-
- OSStatus err = OTRcvURequest(atpEp, &req->fRequest, &flags);
-
- if ( err != kOTNoError )
- {
- delete req;
- if ( err == kOTNoDataErr )
- {
- gIncomingRequestAvailable = false;
- return;
- }
-
- fprintf(stderr, "OTRcvURequest: returned error %d\n", err);
- continue;
- }
- //
- // We need to see if we already have an incoming request matching this.
- //
- TIncomingRequest* next = gIncomingRequestList;
- //
- // Find the incoming request on our list
- //
- while ( next != NULL )
- {
- if ( next->fRequest.sequence == req->fRequest.sequence )
- break;
-
- next = next->fLink;
- }
- //
- // If next == NULL, then this is a brand new request
- //
- if ( next == NULL )
- {
- gTotalBytes += req->fRequest.udata.len;
- req->fNeedsMore = (flags & T_MORE) ? true : false;
- req->fIsXO = (flags & T_ACKNOWLEDGED) ? true : false;
- if ( !req->fNeedsMore )
- {
- req->fNeedsReply = true;
- gIncomingRequests += 1;
- }
- req->fLink = gIncomingRequestList;
- gIncomingRequestList = req;
- }
- else if ( !next->fNeedsMore )
- {
- fprintf(stderr, "OTRcvURequest: BAD BAD - received a duplicate request!!\n");
- delete req;
- }
- else
- {
- gTotalBytes += req->fRequest.udata.len;
- next->fNeedsMore = (flags & T_MORE) ? true : false;
- if ( !(flags & T_MORE) )
- {
- next->fNeedsReply = true;
- gIncomingRequests += 1;
- }
- delete req;
- }
- }
- }
-
- /*******************************************************************************
- ** DoReadUReplies
- ********************************************************************************/
-
- static void DoReadUReplies(EndpointRef atpEp)
- {
-
- OSStatus err;
- OTFlags flags;
-
- while ( true )
- {
- TUnitReply reply;
- //
- // For this sample, we don't really care what the
- // reply is, so we just read it into a global buffer.
- //
- reply.opt.maxlen = 0;
- reply.udata.maxlen = sizeof(gReplyBuffer);
- reply.udata.buf = gReplyBuffer;
-
- err = OTRcvUReply(atpEp, &reply, &flags);
-
- if ( err != kOTNoError )
- {
- if ( err == kOTNoDataErr )
- return;
- if ( err != kETIMEDOUTErr )
- {
- fprintf(stderr, "\nOTRcvUReply: returned error %d\n", err);
- continue;
- }
- }
- //
- // We need to find the matching request
- //
- TOutgoingRequest* prev = NULL;
- TOutgoingRequest* next = gOutgoingRequestList;
- //
- // Find the outgoing request on our list
- //
- while ( next != NULL )
- {
- if ( next->fRequest.sequence == reply.sequence )
- break;
-
- prev = next;
- next = next->fLink;
- }
- if ( next == NULL )
- {
- fprintf(stderr, "\nOTRcvUReply: no matching request for reply\n");
- }
- //
- // If the T_MORE flag is set, we won't remove the request from the list
- // yet. In real life, you need to obviously handle reading the "more"
- // data somewhere rather than on top of the prior data.
- //
- gTotalBytes += reply.udata.len;
- if ( !(flags & T_MORE) )
- {
- if ( prev == NULL )
- {
- gOutgoingRequestList = next->fLink;
- }
- else
- {
- prev->fLink = next->fLink;
- }
- delete next;
- gOutgoingRequests -= 1;
- }
-
- }
- }
-
- /*******************************************************************************
- ** DoSendURequest
- ********************************************************************************/
-
- void DoSendURequest(EndpointRef atpEp, int flag)
- {
- OSStatus err = kOTNoError;
- //
- // If we have the maximum number of outstanding requests, or
- // we are flow controlled - go away for now.
- //
- while ( true )
- {
- if ( gOutgoingRequests >= kMaxRequests || gFlowControlled )
- return;
-
- TOutgoingRequest* req = new TOutgoingRequest;
-
- if ( req == NULL )
- {
- fprintf(stderr, "DoSendURequest: can't allocate outgoing request structure\n");
- return;
- }
-
- req->fHasReply = false;
-
- req->fRequest.addr.buf = (UInt8*)&gPeerAddress;
- req->fRequest.addr.len = kDDPAddressLength;
- req->fRequest.opt.len = 0;
- req->fRequest.udata.buf = (UInt8*)gRequestData;
- req->fRequest.udata.len = sizeof(gRequestData);
- req->fRequest.sequence = ++gReqSequence;
- //
- // Throw it on the list of outgoing requests
- //
- req->fLink = gOutgoingRequestList;
- gOutgoingRequestList = req;
-
- //
- // Send the request
- // We set the gFlowControlled flag now, because if we wait until we
- // get the flow error, we might encounter a race condition where
- // we set the flag after a new T_GODATA event clears it!
- // This is not a problem when we do the sending inside the event handler,
- // which is normally the case in a real application (because the event
- // handler is never reentered).
- //
- gFlowControlled = true;
- gOutgoingRequests += 1;
-
- err = OTSndURequest(atpEp, &req->fRequest, (OTFlags)flag);
-
- if ( err != kOTNoError )
- {
- gTotalBytes += sizeof(gRequestData);
- gOutgoingRequests -= 1;
- if ( err != kOTFlowErr )
- gFlowControlled = false;
- fprintf(stderr, "OTSndURequest completed with error %d\n", err);
- return;
- }
- gFlowControlled = false;
- gDotCount += 1;
- }
- }
-
- /*******************************************************************************
- ** EventHandler
- ********************************************************************************/
-
- pascal void EventHandler(void* contextPtr, OTEventCode event, OTResult result, void* cookie)
- {
- switch ( event )
- {
- case T_REPLYCOMPLETE:
- {
- /*
- * We have a reply that's done. Find it on our list of incoming requests
- * and remove it
- */
- TIncomingRequest* prev = NULL;
- TIncomingRequest* next = gIncomingRequestList;
-
- while ( next != NULL )
- {
- if ( &next->fReply == (TUnitReply*)cookie )
- {
- if ( prev == NULL )
- {
- gIncomingRequestList = next->fLink;
- }
- else
- {
- prev->fLink = next->fLink;
- }
- break;
- }
- prev = next;
- next = next->fLink;
- }
- if ( next == NULL )
- {
- DebugStr("\pEventHandler: T_REPLYCOMPLETE event, but no matching request");
- }
- else
- {
- if ( result != kOTNoError )
- {
- fprintf(stderr, "OTSndUReply: completed with error %d\n", result);
- }
- if ( prev == NULL )
- gIncomingRequestList = next->fLink;
- else
- prev->fLink = next->fLink;
- delete next;
- gIncomingRequests -= 1;
- }
- break;
- }
-
- case T_BINDCOMPLETE:
- case T_UNBINDCOMPLETE:
- case T_RESOLVEADDRCOMPLETE:
- {
- gFunctionEvent = event;
- gFunctionErr = (OSStatus)result;
- gFunctionComplete = true;
- gFunctionCookie = cookie;
- break;
- }
-
- case T_REPLY:
- {
- /*
- * There are incoming replies - read them, then send more requests
- */
- DoReadUReplies((EndpointRef)contextPtr);
- DoSendURequest((EndpointRef)contextPtr, gTestMode == 1 ? T_ACKNOWLEDGED : 0);
- break;
- }
-
- case T_REQUEST:
- {
- /*
- * There are incoming requests - read them, then answer them
- */
- DoReadURequests((EndpointRef)contextPtr);
- DoSendUReplies((EndpointRef)contextPtr);
- break;
- }
-
- case T_GODATA:
- {
- gFlowControlled = false;
- switch ( gTestMode )
- {
- case 1:
- case 2:
- {
- DoSendURequest((EndpointRef)contextPtr, gTestMode == 1 ? T_ACKNOWLEDGED : 0);
- break;
- }
-
- default:
- {
- DoReadURequests((EndpointRef)contextPtr);
- DoSendUReplies((EndpointRef)contextPtr);
- break;
- };
- }
- break;
- }
-
- default:
- {
- DebugStr("\pEventHandler: Unexpected Event!");
- break;
- }
- }
- }
-
- /*******************************************************************************
- ** CreateEndpoint
- ********************************************************************************/
-
- EndpointRef CreateEndpoint()
- {
- EndpointRef atpEp = kOTInvalidEndpointRef;
- OSStatus err = kOTNoError;
-
- do
- {
- //
- // Now create an ATP endpoint synchronously
- //
- atpEp = OTOpenEndpoint(OTCreateConfiguration(kATPName), 0, NULL, &err);
-
- if ( atpEp == kOTInvalidEndpointRef || err != kOTNoError )
- {
- atpEp = kOTInvalidEndpointRef;
- fprintf(stderr,"ERROR: OpenEndpoint(\"atp\") failed with %d\n", err);
- break;
- }
- //
- // Endpoint was created in synchronous mode, but it's always best to be safe
- //
- OTSetSynchronous(atpEp);
- //
- // Install notifier we're going to use for testing
- //
- err = OTInstallNotifier(atpEp, EventHandler, atpEp);
- if ( err != kOTNoError )
- {
- fprintf(stderr, "ERROR: InstallNotifier() failed with %d\n", err);
- break;
- }
-
- ShowFullEndpointData(atpEp);
-
- return atpEp;
-
-
- } while (false);
-
- if ( atpEp != kOTInvalidEndpointRef )
- OTCloseProvider((ProviderRef)atpEp);
-
- return kOTInvalidEndpointRef;
- }
-
- /*******************************************************************************
- ** DoTest
- ********************************************************************************/
-
- void DoTest()
- {
- long waitSequence = 0;
- OSStatus err = kOTNoError;
- Boolean waitingToReply = false;
- EndpointRef atpEp = kOTInvalidEndpointRef;
-
- gOutgoingRequestList = NULL;
- gIncomingRequestList = NULL;
-
- gOutgoingRequests = 0;
- gIncomingRequests = 0;
- gIncomingRequestAvailable = false;
-
- gFlowControlled = false;
-
- do
- {
- /* -------------------------------------------------------------------------
- Create endpoints.
- ------------------------------------------------------------------------- */
-
- atpEp = CreateEndpoint();
- if ( atpEp == kOTInvalidEndpointRef )
- {
- fprintf(stderr, "ATPSample: CreateEndpoint for ATP endpoint failed.\n", err);
- break;
- }
-
- /* -------------------------------------------------------------------------
- Get the test mode
- ------------------------------------------------------------------------- */
-
- fprintf(stderr, "Enter 1=XO Requestor 2=ALO Requestor 3=Responder: ");
- scanf("%x", &gTestMode);
- fprintf(stderr, "Selected Mode is %d.\n", gTestMode);
-
- if ( gTestMode < 1 || gTestMode > 3 )
- break;
-
- /*-------------------------------------------------------------------------
- Bind endpoint.
- ------------------------------------------------------------------------- */
-
- atpEp->SetAsynchronous();
-
- err = DoStaticBind(atpEp, gTestMode == 3 ? kResponderName : kRequestorName);
- if ( err != kOTNoError )
- {
- fprintf(stderr, "ATPSample: bind of ATP endpoint failed (error %d).\n", err);
- break;
- }
-
- /* -------------------------------------------------------------------------
- Prepare to run - If we're the requestor, try to find the responder.
- ------------------------------------------------------------------------- */
-
- if ( gTestMode == 1 || gTestMode == 2 )
- {
- OTTimeStamp stamp;
- Boolean firstTime = true;
-
- OTGetTimeStamp(&stamp);
- do
- {
- err = DoResolveAddr(atpEp, kResponderName, &gPeerAddress);
- if ( err == kOTNoError )
- break;
- if ( firstTime )
- {
- firstTime = false;
- fprintf(stderr, "Waiting for Responder to come on-line.\n");
- fprintf(stderr, "Press mouse button to abort\n");
- }
- /*
- * Delay for a second so we don't flood the net!
- */
- while ( OTElapsedMilliseconds(&stamp) < 1000 )
- {
- if ( Button() )
- break;
- }
- } while ( !Button() );
- if ( err != kOTNoError )
- {
- while ( Button() )
- ;
- break;
- }
-
- fprintf(stderr, "Ready to Send.\n");
- }
- else
- {
- fprintf(stderr, "Size of replies to be sent: %d\n", kTestReplySize);
-
- }
-
- OTGetTimeStamp(&gStartStamp);
- gTotalBytes = 0;
-
- if ( gTestMode != 3 )
- {
- DoSendURequest(atpEp, gTestMode == 1 ? T_ACKNOWLEDGED : 0);
- }
- do
- {
- Boolean needFlush = false;
- if ( gDotCount >= 64 )
- {
- gDotCount -= 64;
- gLineCount += 1;
- fprintf(stderr, ".");
- needFlush = true;
- }
- if ( gLineCount >= 64 )
- {
- UInt32 milliSecs = OTElapsedMilliseconds(&gStartStamp);
-
- gLineCount -= 64;
- fprintf(stderr, ": %lu\n", ((gTotalBytes * 1000)/milliSecs));
- gTotalBytes = 0;
- OTGetTimeStamp(&gStartStamp);
- }
- else if ( needFlush )
- fflush(stderr);
- } while ( !Button() ); // Wait for 'um to press mouse button
-
- //
- // Wait for the button to be unpressed
- //
- while ( Button() )
- ;
-
- fflush(stderr);
- //
- // Cancel all outstanding commands
- //
- OTCancelURequest(atpEp, (OTSequence)0);
- OTCancelUReply(atpEp, (OTSequence)0);
- //
- // Unbind
- //
- fprintf(stderr, "About to Unbind()\n");
- gFunctionComplete = false;
- err = OTUnbind(atpEp);
- if ( err != kOTNoError )
- {
- fprintf(stderr, "ERROR: Unbind() returned %d\n", err);
- break;
- }
- while ( !gFunctionComplete )
- OTIdle();
- if ( gFunctionEvent != T_UNBINDCOMPLETE )
- {
- fprintf(stderr, "OTUnbind: completed with event %08lX instead of %08lX\n",
- gFunctionEvent, T_UNBINDCOMPLETE);
- break;
- }
-
- if ( gFunctionErr != kOTNoError )
- {
- fprintf(stderr, "OTUnbind: completed with error %d\n", gFunctionErr);
- break;
- }
-
- ShowEndpointState(atpEp, "");
-
- } while (false);
-
-
- //
- // Get rid of endpoint.
- //
- if ( atpEp != kOTInvalidEndpointRef )
- OTCloseProvider((ProviderRef)atpEp);
- }
-
- /*******************************************************************************
- ** Initialize OpenTransport and call DoTest function
- ********************************************************************************/
-
- main(int, char**)
- {
- InitGraf(&qd.thePort); // initialize quickdraw so we can use regions
-
- fprintf(stderr, "ATPSample showing usage of ATP.\n\n");
- //
- // Initialize Open Transport
- //
- InitOpenTransport();
- //
- // Run the test
- //
- DoTest();
- //
- // Close Open Transport.
- // Not strictly necessary since it patches _ExitToShell and will
- // clean us up anyway.
- //
- CloseOpenTransport();
-
- fprintf(stderr, "\n\nDone\n");
- return 0;
- };
-
-